#ifndef __INET_WATCHER__
#define __INET_WATCHER__

#include <WinInet.h>

static mm::dllwp_item_t inet_watcher_apis [] = 
{
	{ "wininet.dll", "HttpOpenRequestA",	8 * 4 },
	{ "wininet.dll", "HttpOpenRequestW",	8 * 4 },
	//////////////////////////////////////////////////////////////////////////
	{ "wininet.dll", "InternetOpenUrlA",	6 * 4 },
	{ "wininet.dll", "InternetOpenUrlW",	6 * 4 },
	//////////////////////////////////////////////////////////////////////////
	{ "wininet.dll", "InternetReadFile",	8 * 4 },
	{ "wininet.dll", "InternetWriteFile",	8 * 4 },
	//////////////////////////////////////////////////////////////////////////
	{ "wininet.dll", "InternetConnectA",	8 * 4 },
	{ "wininet.dll", "InternetConnectW",	8 * 4 },
	//////////////////////////////////////////////////////////////////////////
	{ "wininet.dll", "InternetCloseHandle", 1 * 4 },
	{ },
};

struct inet_watcher : mm::basic_watcher_t
{
	typedef std::map<HINTERNET,std::string> hi_to_server_map_t;

	hi_to_server_map_t	_server_map;

	inet_watcher() : mm::basic_watcher_t( inet_watcher_apis )
	{
	}

	virtual long impl_after_call( size_t uid, hk::exec_context * pctx )
	{
		xv::xvalue_t _action;
		if( !_action.asObject() ) return NORMAL_LEAVE;
		printf( "%s\n", mm::api_name(uid) );
		//////////////////////////////////////////////////////////////////////////
		if( mm::is_api(uid, "InternetReadFile") )
		{
			HINTERNET hFile = (HINTERNET)pctx->args[0];
			LPVOID lpBuffer = (LPVOID)pctx->args[1];
			DWORD dwNumberOfBytesToRead = (DWORD)pctx->args[2];
			LPDWORD lpdwNumberOfBytesRead = (LPDWORD)pctx->args[3];
			_action["action"] = mm::api_name(uid);
			_action["payload"]["hi"] = (int64_t)(hFile);
			_action["payload"]["data"] = xv::chunk_t( lpBuffer, *lpdwNumberOfBytesRead );
			//////////////////////////////////////////////////////////////////////////
		}
		else if( mm::is_api(uid, "InternetWriteFile") )
		{
			//////////////////////////////////////////////////////////////////////////
			BOOL fok = (BOOL)pctx->retval();
			if( !fok ) return NORMAL_LEAVE;
			HINTERNET hFile = (HINTERNET)pctx->args[0];
			LPVOID lpBuffer = (LPVOID)pctx->args[1];
			DWORD dwNumberOfBytesToRead = (DWORD)pctx->args[2];
			LPDWORD lpdwNumberOfBytesRead = (LPDWORD)pctx->args[3];
			_action["action"] = mm::api_name(uid);
			_action["payload"]["hi"] = (int64_t)(hFile);
			_action["payload"]["data"] = xv::chunk_t( lpBuffer, *lpdwNumberOfBytesRead );
			//////////////////////////////////////////////////////////////////////////
		}
		else if( mm::is_api(uid, "InternetConnectA" ) || mm::is_api(uid, "InternetConnectW" ))
		{
			HINTERNET hi = (HINTERNET)pctx->retval();
			if( !hi ) return NORMAL_LEAVE;
			xv::xvalue_t server = get_thread_data( "inetmon" );
			if( !server.isMBS() ) return NORMAL_LEAVE;
			std::string tmp = (const char*)server;
			mm::sync_t _sync_(lock());
			_server_map[hi] = tmp;
		}
		return NORMAL_LEAVE;
	}
	//////////////////////////////////////////////////////////////////////////
	virtual size_t impl_before_call( size_t uid, hk::exec_context * pctx, size_t deny_count )
	{
		printf( "%s\n", mm::api_name(uid) );
		xv::xvalue_t _action;
		if( !_action.asObject() ) return true;
		//////////////////////////////////////////////////////////////////////////
		if( mm::is_api(uid, "HttpOpenRequestA") )
		{
			HINTERNET hi = (HINTERNET)pctx->args[0];
			std::string server;
			{
				mm::sync_t _sync_(lock());
				hi_to_server_map_t::iterator it = _server_map.find(hi);
				server = ( it == _server_map.end() ) ? "" : it->second;
			}
			_action["action"] = mm::api_name(uid);
			_action["payload"]["verb"] = tcs::as_to_as(pctx->args[1]);
			_action["payload"]["object"] = server + tcs::as_to_as(pctx->args[2]);
			_action["payload"]["version"] = tcs::as_to_as(pctx->args[3]);
			_action["payload"]["refers"] = tcs::as_to_as(pctx->args[4]);
		}
		else if( mm::is_api(uid, "HttpOpenRequestW") )
		{
			HINTERNET hi = (HINTERNET)pctx->args[0];
			std::string server;
			{
				mm::sync_t _sync_(lock());
				hi_to_server_map_t::iterator it = _server_map.find(hi);
				server = ( it == _server_map.end() ) ? "" : it->second;
			}
			_action["action"] = mm::api_name(uid);
			_action["payload"]["verb"] = tcs::ws_to_as(pctx->args[1]);
			_action["payload"]["object"] = server + tcs::ws_to_as(pctx->args[2]);
			_action["payload"]["version"] = tcs::ws_to_as(pctx->args[3]);
			_action["payload"]["refers"] = tcs::ws_to_as(pctx->args[4]);
		}
		else if( mm::is_api(uid, "InternetOpenUrlA") )
		{
			_action["action"] = mm::api_name(uid);
			_action["payload"]["url"] = (const char *)pctx->args[1];
			_action["payload"]["headers"] = (const char *)(pctx->args[2]);
		}
		else if( mm::is_api(uid, "InternetOpenUrlW") )
		{
			_action["action"] = mm::api_name(uid);
			_action["payload"]["url"] = tcs::ws_to_as(pctx->args[1]);
			_action["payload"]["headers"] = tcs::ws_to_as(pctx->args[2]);
		}
		else if( mm::is_api(uid, "InternetConnectA" ) )
		{
			std::string server;
			tcs::xprintf( server, "%s:%d", (CONST CHAR*)(pctx->args[1]), (int32_t)(pctx->args[2]) );
			set_thread_data( "inetmon", server );
			return true;
		}
		else if( mm::is_api(uid, "InternetConnectW" ) )
		{
			std::string server;
			std::string server_ = tcs::ws_to_as(pctx->args[1]);
			int32_t		port_ = (int32_t)(pctx->args[2]);
			tcs::xprintf( server, "%s:%d", server_.c_str(), port_ );
			set_thread_data( "inetmon", server );
			return true;
		}
		else if( mm::is_api(uid, "InternetCloseHandle") )
		{
			HINTERNET hi = (HINTERNET)pctx->args[0];
			mm::sync_t _sync_(lock());
			_server_map.erase( hi );
			return true;
		}
		else 
		{
			return true;
		}
		throw_event( _action );
		return true;
	};

};


#endif